#pragma once

#include <iostream>
#include <queue>
#include <string>
#include <vector>
#include <pthread.h>
#include "Thread.hpp"
#include "LockGuard.hpp"
#include "Log.hpp"

const int default_thread_num = 5;

class ThreadData
{
public:
    ThreadData(std::string thread_name = "new thread")
        : _thread_name(thread_name)
    {}

    ~ThreadData() 
    {}
public:
    std::string _thread_name;
};

template<class T>
class ThreadPool
{
private:
    ThreadPool(int thread_num = default_thread_num) : _thread_num(thread_num) 
    {
        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_cond, nullptr);
        for(int i = 0; i < _thread_num; i++) 
        {
            std::string threadname = "thread-" + std::to_string(i+1);
            ThreadData td(threadname);
            _threads.emplace_back(threadname, 
                std::bind(&ThreadPool<T>::threadRoutine, this, std::placeholders::_1), td); //移动构造
            log.message(INFO, "%s ready ...", threadname.c_str());

            // Thread<ThreadData> t(threadname, 
            //     std::bind(&ThreadPool<T>::threadRoutine, this, std::placeholders::_1), td);
            // _threads.push_back(t);
        }
    }
    ThreadPool(const ThreadPool<T>& tp) = delete;
    const ThreadPool<T>& operator=(const ThreadPool<T> tp) = delete;
public:
    static ThreadPool<T>* getInstance()
    {
        if(_instance == nullptr)
        {
            LockGuard lockGuard(&_singleton_mutex);
            if(_instance == nullptr) 
            {
                _instance = new ThreadPool<T>();
            }
        }
        return _instance;
    }

    void start()
    {
        for(auto& a : _threads) 
        {
            a.start();
            log.message(DEBUG, "%s start ...", a.getThreadName().c_str());
        }
    }

    void submit(const T& task)
    {
        {
            LockGuard lockGuard(&_mutex);
            _q.push(task);
            notify();
            log.message(DEBUG, "task submit ...");
        }
    }

    ~ThreadPool() 
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }
private:
    void threadRoutine(ThreadData& td)
    {
        while(true)
        {
            T task;
            {
                LockGuard lockGuard(&_mutex);
                while(_q.empty())
                {
                    wait();
                }
                task = _q.front();
                _q.pop();
            }
            task();
        }
    }

    void wait()
    {
        pthread_cond_wait(&_cond, &_mutex);
    }

    void notify()
    {
        pthread_cond_signal(&_cond);
    }
private:
    static ThreadPool<T>* _instance;
    std::queue<T> _q;
    std::vector<Thread<ThreadData>> _threads;
    int _thread_num;
    pthread_mutex_t _mutex;
    static pthread_mutex_t _singleton_mutex;
    pthread_cond_t _cond;
};

template<class T>
ThreadPool<T>* ThreadPool<T>::_instance = nullptr;

template<class T>
pthread_mutex_t ThreadPool<T>::_singleton_mutex = PTHREAD_MUTEX_INITIALIZER;